package com.test.springboot.springBootUtils.redis;

import java.util.UUID;
import java.util.concurrent.TimeUnit;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;

public class MyDistributedlock {

	@Autowired
	private StringRedisTemplate stringRedisTemplate;

	public String deductStock() {
		final String lockKey = "lockKey";// 分布式锁的key
		final String lockValue = UUID.randomUUID().toString();
 
		try {
			// 使用Redis 的 setnx 命令实现分布式锁
			// Boolean result=stringRedisTemplate.opsForValue().setIfAbsent(lockKey,
			// "lockValue");
			// 3.代码走到这里进程被 kill 了 设置key 成功，但是过期时间没有设置成功，代码宕机(相当于key 没有过期时间)
			// stringRedisTemplate.expire(lockKey, 10,
			// TimeUnit.SECONDS);//2.JVM进程被kill掉，代码没有走到 finally锁未释放 解决办法 在key 上面添加一个过期时间

			// 解决3的办法 可以将设置key 和 设置超时时间的命令整合成一个命令
			Boolean result = stringRedisTemplate.opsForValue().setIfAbsent(lockKey, lockValue, 10, TimeUnit.SECONDS);

			if (!result) {
				return "系统繁忙 请稍后再试";
			}

			// 这里处理业务代码
			return "Success";
		} catch (Exception e) {
			// 这里业务代码出现异常 开始异常的补偿
			return "fail";
		} finally {
			// 解决 锁失效的问题
			if (lockValue.equalsIgnoreCase(stringRedisTemplate.opsForValue().get(lockKey))) {
				// 1.业务代码报错，锁未释放 解决办法 加 try finally
				stringRedisTemplate.delete(lockKey);
			}
		}

		/**
		 * 锁失效的描述:
		 * 前提: 锁的失效时间是10秒，假如 业务代码执行15秒  那么Redis会自动将这个key 清除掉 (即锁被释放) ，这时候第二个线程拿到锁开始执行  ，当第一个15秒执行完成之后
		 * 在finally 中将第二个锁进行释放 这样会造成雪崩  后面锁失效的局面。
		 * 根本原因：我自己线程加的锁，被其他线程释放了
		 * 解决办法:
		 *  当前线程创建一个唯一的标识 在删除锁的时候判断当前需要删除的锁是否为自己加的锁
		 *  
		 * 
		 * 
		 */
		
		/**
		 * Redis key的设置时间问题:
		 * Redis 的key 的设置时间  到底应该设置多长，如果太短，业务代码没有执行完成，但是锁已经被Redis释放，如果太长 中间宕机 当前锁释放需要等待很长时间
		 * 解决方法：
		 * 	锁续命，在开始执行业务代码的时候 开启一个线程 监控当前锁的过期时间，如果快要过期 ，但是业务代码还未执行完毕，这时候重新设置当前key 的过期时间,
		 * 锁续命的时候大概就是 当前设置key 过期时间的三分之一
		 *  解决方法有 :Redission 解决
		 */

	}

}
